﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Text.RegularExpressions;

namespace Science.Mathematics.Algebra
{
    public class Interval
    {
        #region Private Data Members

        private double _start = 0;
        private double _end = 0;
        private bool _startOpened = false;
        private bool _endOpened = false;

        #endregion

        #region Properties

        public double Start
        {
            get
            {
                return _start;
            }
            set
            {
                if (Double.IsInfinity(value))
                    StartOpened = false;

                _start = value;                
            }
        }

        public double End
        {
            get
            {
                return _end;
            }
            set
            {
                if (Double.IsInfinity(value))
                    StartOpened = false;

                if (value < Start)
                    throw new ArgumentException("The End of the interval must be >= the start of the interval");
                _end = value;                
            }
        }

        public bool StartOpened
        {
            get
            {
                return _startOpened;
            }
            set
            {
                if (Double.IsInfinity(Start) && value == false)
                    throw new ArgumentException("The infinity start of interval cannot be closed interval");
                _startOpened = value;
            }
        }

        public bool EndOpened
        {
            get
            {
                return _endOpened;
            }
            set
            {
                if (Double.IsInfinity(End) && value == false)
                    throw new ArgumentException("The infinity end of interval cannot be closed interval");
                _endOpened = value;
            }
        }

        #endregion

        #region Constructors

        public Interval()
		{
			Start = 0; End = 0;
            StartOpened = false;
            EndOpened = false;
		}

        public Interval(String intervalStr)
        {
            int index = 0;
            Interval inetrval = Interval.Parse(intervalStr, ref index);
            Start = inetrval.Start;
            End = inetrval.End;
            StartOpened = inetrval.StartOpened;
            EndOpened = inetrval.EndOpened;            
        }

        public Interval(double start, double end, bool startOpened = false, bool endOpened = false)
		{
			Start = start; End = end;
            StartOpened = startOpened;
            EndOpened = endOpened;
		}

        #endregion

        #region Public Methods

        public bool Include(double x)
        {
            bool result = true;
            result &= StartOpened ? x > Start : x >= Start;
            result &= EndOpened ? x < End : x <= End;
            return result;
        }

        #endregion

        #region Static Public Function

        public static Interval Parse(String str, ref int index)
        {
            if (index >= str.Length)
                throw new InvalidOperationException("Invalid interval string");

            Interval interval = new Interval();
            Match match = Regex.Match(str, @"([\[\(])\s*(-?\d+|-\bInf\b)\s*,\s*(-?\d+|\bInf\b)\s*([\]\)])", RegexOptions.IgnoreCase);
            if (match.Success)
            {
                index = match.Index + match.Length;
                interval.StartOpened = match.Groups[1].Value == "(";
                interval.EndOpened = match.Groups[4].Value == ")";
                interval.Start = match.Groups[2].Value.Equals("-Inf") ? Double.NegativeInfinity : double.Parse(match.Groups[2].Value);
                interval.End = match.Groups[3].Value.Equals("Inf") ? Double.PositiveInfinity : double.Parse(match.Groups[3].Value);
            }
            else
                throw new InvalidOperationException("Invalid interval string");
            return interval;
        }

        public static bool TryParse(String str, ref int index, ref Interval interval)
        {
            try
            {
                interval = Interval.Parse(str, ref index);
                return true;
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region Object Virtual Functions

        public override bool Equals(object obj)
        {
            Interval interval = obj as Interval;
            return Start == interval.Start && End == interval.End && StartOpened == interval.StartOpened
                && EndOpened == interval.EndOpened;
        }

        public override int GetHashCode()
        {
            return Start.GetHashCode() + End.GetHashCode() + StartOpened.GetHashCode() + EndOpened.GetHashCode();
        }

        public override string ToString()
        {
            return (StartOpened ? "(" : "[") + (Double.IsInfinity(Start) ? "-Inf" : Start.ToString()) + "," +
                (Double.IsInfinity(End) ? "Inf" : End.ToString()) + (EndOpened ? ")" : "]");
        }

        #endregion

        #region Overloaded Operator

        #region Conversion Operators

        public static implicit operator Interval(String str)
        {
            int index = 0;
            return Interval.Parse(str, ref index);
        }

        public static explicit operator String(Interval x)
        {
            return x.ToString();
        }

        #endregion

        #endregion
    }
}
